#pragma once
#include "net_common.hpp"
#include "net_message.hpp"

/* Requried C++ 17 */
// using std::scoped_lock

namespace dw
{
    namespace net
    {
        template <typename T>
        class tsdeque
        {
            // Thread-Safe deque
        protected:
            std::mutex muxDeque;
            std::deque<T> deq;

            std::mutex muxBlocking;
            std::condition_variable cvBlocking;

        public:
            tsdeque() = default;
            tsdeque(const tsdeque<T> &) = delete;
            virtual ~tsdeque() { clear(); }

            const T &front()
            {
                std::scoped_lock lock(muxDeque);
                return deq.front();
            }

            const T &back()
            {
                std::scoped_lock lock(muxDeque);
                return deq.back();
            }

            void push_front(const T &item)
            {
                std::scoped_lock lock(muxDeque);
                deq.emplace_front(std::move(item));

                std::unique_lock<std::mutex> ul(muxBlocking);
                cvBlocking.notify_one();
            }

            void push_back(const T &item)
            {
                std::scoped_lock lock(muxDeque);
                deq.emplace_back(std::move(item));

                std::unique_lock<std::mutex> ul(muxBlocking);
                cvBlocking.notify_one();
            }

            T pop_front()
            {
                std::scoped_lock lock(muxDeque);
                T front = std::move(deq.front());
                deq.pop_front();
                return front;
            }

            T pop_back()
            {
                std::scoped_lock lock(muxDeque);
                T back = std::move(deq.back());
                deq.pop_back();
                return back;
            }

            bool empty()
            {
                std::scoped_lock lock(muxDeque);
                return deq.empty();
            }

            uint32_t count()
            {
                std::scoped_lock lock(muxDeque);
                return deq.size();
            }

            void clear()
            {
                std::scoped_lock lock(muxDeque);
                return deq.clear();
            }

            void wait()
            {
                while (empty())
                {
                    std::unique_lock<std::mutex> ul(muxBlocking);
                    cvBlocking.wait(ul);
                }
            }

            friend std::ostream& operator << (std::ostream& os, tsdeque<T>& tsdeq)
            {
                std::scoped_lock lock(tsdeq.muxDeque);
                os << "tsdeq size: " << tsdeq.deq.size()  << std::endl;
                if (tsdeq.deq.size()) {
                    os << "     front: " << tsdeq.deq.front() << std::endl;
                    os << "      back: " << tsdeq.deq.back()  << std::endl;
                }
                return os;
            }
        };
    }
}